#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import argparse
import json
import sys
from datetime import datetime, timezone
from pathlib import Path
from uuid import uuid4


def rename_files_in_directory(directory: Path, operations: list[str]) -> None:
    files = sorted((f for f in directory.iterdir() if f.is_file()), key=lambda p: p.name)
    if not files:
        return  # 空目录跳过

    dir_name = directory.name

    if len(files) == 1:
        original_file = files[0]
        suffix = ''.join(original_file.suffixes)
        new_name = f"{dir_name}{suffix}"

        if new_name == original_file.name:
            print(f"[跳过] {directory}: {original_file.name} 已符合规则")
            return

        target_path = directory / new_name
        if target_path.exists():
            raise FileExistsError(f"[冲突] {target_path} 已存在，无法重命名 {directory / original_file.name}")

        original_file.rename(target_path)
        operations.append({
            "directory": str(directory.resolve()),
            "original": original_file.name,
            "renamed": new_name,
        })
        print(f"[1个文件] {directory}: {original_file.name} -> {new_name}")

    else:
        temp_records = []
        for idx, original_file in enumerate(files):
            temp_name = f".__tmp__{uuid4().hex}__{idx}"
            temp_path = directory / temp_name
            original_file.rename(temp_path)
            temp_records.append({
                "temp_path": temp_path,
                "original_name": original_file.name,
                "final_name": f"{dir_name}-{original_file.name}",
                "state": "temp",
            })

        try:
            for record in temp_records:
                final_path = directory / record["final_name"]
                if final_path.exists():
                    raise FileExistsError(
                        f"[冲突] {final_path} 已存在，无法重命名 {directory / record['original_name']}"
                    )

                record["temp_path"].rename(final_path)
                record["state"] = "final"

                if record["final_name"] != record["original_name"]:
                    operations.append({
                        "directory": str(directory.resolve()),
                        "original": record["original_name"],
                        "renamed": record["final_name"],
                    })

                print(f"[多个文件] {directory}: {record['original_name']} -> {record['final_name']}")
        except Exception as exc:
            # 回滚
            for record in temp_records:
                if record["state"] == "final":
                    (directory / record["final_name"]).rename(directory / record["original_name"])
                else:
                    if record["temp_path"].exists():
                        record["temp_path"].rename(directory / record["original_name"])
            raise exc


def apply_mode(root_args: list[str], implicit_mode: bool) -> None:
    operations: list[dict] = []

    if implicit_mode:
        root_path = Path(".").resolve()
        targets = sorted((p for p in root_path.iterdir() if p.is_dir()), key=lambda p: p.name)
        if not targets:
            print(f"[提示] {root_path} 下没有需要处理的子目录。")
            return
        for target in targets:
            try:
                rename_files_in_directory(target, operations)
            except Exception as err:  # 捕获所有异常以继续处理其他目录
                print(f"[错误] 处理 {target} 时出现异常：{err}", file=sys.stderr)
    else:
        for root in root_args:
            root_path = Path(root).resolve()
            if not root_path.exists():
                print(f"[错误] {root_path} 不存在。", file=sys.stderr)
                continue
            if not root_path.is_dir():
                print(f"[跳过] {root_path} 不是目录。", file=sys.stderr)
                continue
            try:
                rename_files_in_directory(root_path, operations)
            except Exception as err:
                print(f"[错误] 处理 {root_path} 时出现异常：{err}", file=sys.stderr)

    if not operations:
        print("[完成] 没有需要重命名的文件，未生成备份。")
        return

    timestamp = datetime.now(timezone.utc).astimezone().strftime("%Y%m%d_%H%M%S")
    backup_file = Path.cwd() / f"rename_backup_{timestamp}.json"
    backup_data = {
        "version": 1,
        "created_at": datetime.now(timezone.utc).isoformat(),
        "working_directory": str(Path.cwd()),
        "operations": operations,
    }
    backup_file.write_text(json.dumps(backup_data, ensure_ascii=False, indent=2), encoding="utf-8")
    print(f"[备份] 已写入：{backup_file}")


def restore_from_backup(backup_file: Path, dry_run: bool = False) -> None:
    if not backup_file.exists() or not backup_file.is_file():
        raise FileNotFoundError(f"备份文件 {backup_file} 不存在或不是文件。")

    data = json.loads(backup_file.read_text(encoding="utf-8"))
    operations = data.get("operations", [])
    if not operations:
        print("[提示] 备份文件中没有记录可恢复的重命名。")
        return

    for op in reversed(operations):
        directory = Path(op["directory"])
        renamed_name = op["renamed"]
        original_name = op["original"]

        src = directory / renamed_name
        dst = directory / original_name

        if not directory.exists():
            print(f"[警告] 目录 {directory} 不存在，跳过。", file=sys.stderr)
            continue
        if not src.exists():
            print(f"[警告] {src} 不存在，跳过。", file=sys.stderr)
            continue
        if dst.exists():
            raise FileExistsError(f"[冲突] 无法恢复 {src} -> {dst}，目标已存在。")

        print(f"[恢复]{' [dry-run]' if dry_run else ''} {src} -> {dst}")
        if not dry_run:
            src.rename(dst)

    if dry_run:
        print("[完成] dry-run 模式：未执行实际重命名。")
    else:
        print("[完成] 已根据备份恢复原始文件名。")


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="单文件目录 -> 目录名+后缀；多文件目录 -> 目录名-原文件名，并生成可恢复的备份。"
    )
    parser.add_argument(
        "roots",
        nargs="*",
        default=["."],
        help="需要处理的目录（可多个）。不传参数时默认处理当前目录下的所有一级子目录。",
    )
    parser.add_argument(
        "--restore",
        metavar="BACKUP_FILE",
        help="使用给定的备份文件恢复原始文件名。",
    )
    parser.add_argument(
        "--dry-run",
        action="store_true",
        help="仅在 --restore 模式下使用，查看将要执行的恢复动作但不真正重命名。",
    )
    return parser.parse_args()


def main() -> None:
    args = parse_args()

    if args.restore:
        restore_from_backup(Path(args.restore).resolve(), dry_run=args.dry_run)
    else:
        if args.dry_run:
            print("[警告] --dry-run 仅在 --restore 模式下有效。", file=sys.stderr)
        implicit_mode = len(sys.argv) == 1  # 未显式传入参数 -> 处理当前目录的一级子目录
        apply_mode(args.roots, implicit_mode=implicit_mode)


if __name__ == "__main__":
    main()